﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using K9Nano.Domain;
using K9Nano.Runtime;
using K9Nano.Share.Exceptions;
using Microsoft.Extensions.Logging;

namespace K9Nano.Application
{
    public class CurdApplicationService<TEntity, TCreateDto, TEditDto, TItemDto, TPagedInputDto, TKey>: K9ApplicationService, ICurdApplicationService<TEntity, TCreateDto, TEditDto, TItemDto, TPagedInputDto, TKey> 
        where TEntity : class, IEntity<TKey>
        where TEditDto : IDto<TKey>
        where TItemDto : IDto<TKey>
        where TPagedInputDto : IPagedInputDto<TEntity>
        where TKey : IEquatable<TKey>
    {
        public CurdApplicationService(IUnitOfWork uow, IMapper mapper, ISessionContext sessionContext, ILoggerFactory loggerFactory) : base(uow, mapper, sessionContext, loggerFactory)
        {
        }

        public virtual async Task<TItemDto> CreateAsync(TCreateDto dto, CancellationToken cancellation = default)
        {
            var entity = Mapper.Map<TEntity>(dto);
            var repository = Uow.Repository<TEntity, TKey>();
            await repository.InsertAsync(entity, cancellation);
            var result = Mapper.Map<TItemDto>(entity);
            return result;
        }

        public virtual async Task<TItemDto> UpdateAsync(TEditDto dto, CancellationToken cancellation = default)
        {
            var entity = Mapper.Map<TEntity>(dto);

            if (entity.IsTransient())
            {
                throw new K9UserFriendlyException("Id 参数无效");
            }

            var repository = Uow.Repository<TEntity, TKey>();
            await repository.UpdateAsync(entity, cancellation);
            var result = Mapper.Map<TItemDto>(entity);
            return result;
        }

        public virtual async Task<PagedResultDto<TItemDto>> ListAsync(TPagedInputDto input,
            CancellationToken cancellation = default)
        {
            var repository = Uow.Repository<TEntity, TKey>();
            var count = await repository.CountAsync(input.Predicate(), cancellation);
            if (count <= 0)
            {
                return new PagedResultDto<TItemDto>();
            }
            var entities = await repository.GetPagedListAsync(input.Predicate(), input.OrderBy(), input.Skip(), input.PageSize, cancellation);
            var result = Mapper.Map<List<TItemDto>>(entities);
            return new PagedResultDto<TItemDto>(count, result);
        }

        public virtual async Task DeleteAsync(TKey id, CancellationToken cancellation = default)
        {
            var repository = Uow.Repository<TEntity, TKey>();
            var entity = await repository.GetAsync(id, cancellation);
            if (entity == null)
            {
                return;
            }
            await repository.DeleteAsync(entity, true, cancellation);
        }

        public virtual async Task<TItemDto> GetAsync(TKey id, CancellationToken cancellation = default)
        {
            var repository = Uow.Repository<TEntity, TKey>();
            var entity = await repository.GetAsync(id, cancellation);
            if (entity == null)
            {
                return default;
            }

            return Mapper.Map<TItemDto>(entity);
        }
    }
}